Marginal → X-Plane Scenery → Tools → GroundTraffic

## GroundTraffic kit for X-Plane®

### Overview

This kit allows X-Plane scenery designers to add animated ground vehicle traffic to airport scenery packages. It requires X-Plane 10.0 or later.

### How it works

This kit supplies a plugin that you can distribute with your scenery package. The plugin reads instructions from a text file that you create (and which you should also distribute with your scenery package) and animates X-Plane scenery objects according to those instructions. The objects to be animated can come from X-Plane's built-in library, from an add-on object library (e.g. OpenSceneryX), or from your scenery package.

### Installation

- Create a folder named `plugins` within your scenery package folder.
- Copy the `GroundTraffic` folder from this kit into the `plugins` folder.

### Animation instructions

Use Notepad, TextEdit, or any other text editor to create a plain text file named `GroundTraffic.txt` in your scenery package folder. Save this blank file with an “ANSI”, “Western” or “UTF-8” encoding. (If using TextEdit, you may have to first choose `Format → Make Plain Text` to see those choices).

Add a “Header” statement to this file, followed by one or more “Routes”:

#### Header

The Header statement tells the plugin the location of your airport. It should be of the form “ICAO lat lon”, where:

ICAO
  Four-character ICAO airport code.
lat & lon
  A location near the centre of your airport. The control tower is often a good choice.

For example Seattle-Tacoma airport tower:

    KSEA 	47.4557260 -122.3013928

#### Route

A Route statement starts a new animation. It should be of the form “`route` speed offset heading object”, where:

speed
  The speed of the animated object, in km/h.
offset
  Offset in metres to shift the object forwards before animation.
heading
  Rotation in degrees to be applied to the object before animation.
object
  The name of the animated object. This can be the name of a library object from X-Plane's built-in library or from an add-on library, the name of an object in your scenery package, or the name of a “Train”.

For the animation to look correct objects should be modelled such that they are pointing North and are roughly centered at their origin (since the plugin rotates them about their origin).

- If using your own objects, design them so they are pointing North (i.e. along the positive green axis in SketchUp or Blender), sitting at or just slightly below ground level to allow for sloped terrain, and centered at or a little forward of their axes' origin. Use `0` for the “offset” and “heading” values in this statement.
- Objects in X-Plane's built-in library tend to be centered at their origin but are not all pointing North; supply a “heading” value to rotate such objects.
- Objects in the OpenSceneryX library all point North but are not centered at their origin; supply an “offset” value of at least half the length of the object to correct for this.
- For trains of objects, supply desired “offset” and “heading” values in the train Car statement; the values supplied here are not used.

For example, a route using an object from the scenery package, travelling at 30km/h (~18mph):

    route	30	0	0	objects/airport_fuel_truck.obj
    ...

A route using an object from X-Plane 10's built-in library, travelling at 24km/h (~15mph), to be rotated 180°:

    route	24	0	180	lib/airport/Ramp_Equipment/Luggage_Truck.obj
    ...

A route using an object from the OpenSceneryX library, travelling at 20km/h (~12mph), shifted forwards 2.5m so that it rotates about its center:

    route	20	2.5	0	opensceneryx/objects/airport/vehicles/loaders/6.obj
    ...

The Route statement should be preceded by a blank line and should be followed by a sequence of “waypoints” and “commands”. The Route ends with a blank line.

#### Waypoint

Animated objects move from one waypoint to the next at the speed specified in the Route statement. When the object passes the last waypoint it proceeds towards the first waypoint, forming a circular route (unless you specify a “reverse” command).

Waypoints should be of the form “lat lon”. For example:

    ...
    47.4484450 -122.3001950
    ...

You can use OverlayEditor or WED to map out your route, typing the the waypoint locations into `GroundTraffic.txt` as you go. You may also want to consider placing a “line” (e.g. one of `opensceneryx/lines/airport/zone_outlines/...`) in OverlayEditor or WED to record your route; this will make it easier if you want to adjust your route in the future. But remember to remove or hide these lines before publishing your scenery package.

Objects will try to avoid crashing into each other by waiting at a waypoint if an object on another route is crossing ahead. So where routes cross don't leave a large distance between the waypoints on either side of the crossing point.

#### Pause command

The Pause command instructs the animated object to wait at the preceding waypoint for a while before moving on. Optionally, you can cause the value of a DataRef to change while the animated object is waiting, and change back before it proceeds.

The Pause command should be of the form “`pause` time `set` name slope curve duration”, where:

time
  Time to wait, in seconds.
`set` name slope curve duration (optional)
  As for the “Set” command.

For example, waiting at a waypoint for 30 minutes:

    ...
    47.4629025 -122.3020905
    pause 1800
    ...

Waiting at a waypoint for one minute while setting the value of the DataRef `marginal/groundtraffic/var[0]` such that it rises from 0 to 1 over five seconds, stays at 1, then starts going back to 0 five seconds before the object proceeds:

    ...
    47.4380074 -122.3040742
    pause 60 set var[0] rise linear 5
    ...

#### At command

The At command instructs the animated object to wait at the preceeding waypoint until a particular time of day. It should be of the form “`at` HH:MM `on` days”, where:

HH:MM
  Times of day at which to proceed with the animation. Up to 24 times can be specified, in local time, and using a 24 hour clock.
days (optional)
  Days on which to proceed with the animation. Specified in English.

For example, an animation that waits until 8am:

    ...
    47.4559849 -122.3028592
    at 08:00
    ...

An animation that waits until 8am or 8pm on a weekday:

    ...
    47.4559849 -122.3028592
    at 08:00 20:00 on Mon Tue Wed Thu Fri
    ...

#### When command

The When command instructs the animated object to wait at the preceeding waypoint until a DataRef is within a specified range. (This command is similar in concept to `ANIM_show` in `.obj` files). It should be of the form “`when` name from to”, where:

name
  The name of a DataRef published by the sim or by another plugin. To access an array DataRef, append the array index within square brackets “`[n]`” to the name.
from to
  Range of values for the DataRef for which the animation proceeds.

For example, an animation that only takes place in daylight:

    ...
    47.4559849 -122.3028592
    when sim/graphics/scenery/percent_lights_on 0 0.2
    ...

#### Backup command

The Backup command instructs the animated object to reverse from the preceding waypoint as far as the next waypoint, and thereafter move forward as normal. If the preceding waypoint also has a Pause command the object points forwards while waiting, and then backs-up after waiting.

The Backup command just consists of the word “`backup`”.

For example backing-up into a parking spot:

    ...
    47.4348468 -122.3034628
    backup
    47.4349236 -122.3034600
    pause 60
    47.4348468 -122.3034628
    ...

Backing-up after a pause:

    ...
    47.4379349 -122.3040074
    47.4380074 -122.3040742
    pause 60
    backup
    47.4379349 -122.3040074
    ...

For best results the waypoints before and after backing-up should be at the same location, as in the above examples, or at least in a straight line.

#### Reverse command

Normally, when the animated object passes the last waypoint it proceeds towards the first waypoint, forming a circular route. The Reverse command instead instructs the object to change direction at the last waypoint and re-trace its steps.

The Reverse command just consists of the word “`reverse`” and must be at the end of the Route. For example:

    ...
    reverse

Note: You can't use the Reverse command in a route that contains Backup commands.

#### Set command

The Set command causes the value of a DataRef to change when the animated object arrives at the preceding waypoint. It should be of the form “`set` name slope curve duration”, where:

name
  `var[0]` ... `var[9]` or the name of a custom DataRef.
slope
  `rise` or `fall` - whether the value the DataRef rises from 0.0 to 1.0, or falls from 1.0 to 0.0.
curve
  `linear` or `sine` - whether the value the DataRef changes linearly or like ∫ over the duration.
duration
  The time in seconds that the DataRef takes to change value. This can be `0` if you want the DataRef to change value instantly.

If you specify a name of the form `var[n]` then this command controls the “array” DataRef `marginal/groundtraffic/var[n]`, which can be used to drive animations in a 3D modelling application that supports X-Plane animations. Each object will see values for this DataRef that are specific to the route that the object is following; so it is safe to re-use this DataRef across different objects and routes. For example, within one `GroundTraffic.txt` file, you could use `marginal/groundtraffic/var[0]` to control an animated belt in one or more beltloader objects, and also use `marginal/groundtraffic/var[0]` to control a refueling animation in one or more fuel truck objects. However, because the values returned for this DataRef are specific to a route, you can't usefully use it in a static object that is placed conventionally within an X-Plane scenery package.

Alternatively you can specify the name of a custom DataRef of your choice, but which should be unique to this scenery package. This DataRef can be used to animate objects that are used in a route and/or static objects that are placed conventionally within an X-Plane scenery package. Because all objects (either static or animated by this plugin) will see the same value for your custom DataRef it doesn't make sense to use the same custom DataRef in more than one route.

For example, setting the value of the DataRef `marginal/groundtraffic/var[1]` such that it rises from 0 to 1 over five seconds on hitting a waypoint:

    ...
    47.4380074 -122.3040742
    set var[1] rise linear 5
    ...

Setting the value of a custom DataRef such that it falls from 1 to 0 over two seconds on hitting a waypoint:

    ...
    47.4348578 -122.3034624
    set my/custom/dataref fall sine 2
    ...

The “Pause ... set” command also supports changing the value of a custom DataRef when the animated object arrives at a waypoint. The “Pause ... set” command restores the DataRef to its previous value before the object moves on from the waypoint; this command does not.

#### Comment

Lines starting with “`#`” are treated as comments and are ignored. For example:

    # This is a comment

#### Debug

The Debug statement tells the plugin to plot out and number all waypoints. This is useful for checking that your waypoints are at the locations that you intended. The Debug statement just consists of the word “`debug`” and should be preceded by a blank line:

    debug

#### Train

Instead of animating a single object along a Route, you can animate a number of objects to follow each other in succession like train cars/coaches follow their locomotive.

A Train statement starts a description of a train of connected objects. It should be of the form “`train` name”, where:

name
  The name of your train. Use this name in a Route statement to animate this train. To avoid any ambiguity don't name your train with the same name as a library object or an object in your scenery package.

For example a suitable name for a train consisting of a tug and four luggage carts:

    train	marginal/luggage_train_4
    ...

The Train statement should be preceded by a blank line and should be followed by a list of the “Car” statements. The Train description ends with a blank line.

#### Car

A train Car statement should be of the form “lag offset heading object”, where:

lag
  Distance in metres that the object should lag behind the lead object in the train.
offset
  Offset in metres to be applied to the object before animation.
heading
  Rotation in degrees to be applied to the object before animation.
object
  The name of the animated object. This can be the name of a library object from X-Plane's built-in library or from an add-on library, or the name of an object in your scenery package. It should not be the name of this or another Train.

To determine how far an object should lag behind the the lead object you can open/import the objects in your 3D modelling application and measure how far (in metres) you have to move the object behind the lead object for it to appear to “link up” correctly. If either object needs an offset to be centered on its axes then adjust for this before measuring. Repeat the process for further objects.

For example two objects, both of which need to be rotated 180°, the second of which is 3.36m behind the first:

    ...
    0	0	180	lib/airport/Ramp_Equipment/Luggage_Truck.obj
    3.36	0	180	lib/airport/Ramp_Equipment/Luggage_Cart.obj
    ...

The plugin animates train Car objects as if they are connected to each other so don't leave huge gaps between Cars. In particular, if you want to send two or more separate objects down the same route don't (ab)use the Train statement; just duplicate the route and change the order of the waypoints in the copy/copies to space out the objects.

### Animation DataRefs

The plugin publishes the following DataRefs that you can use to create animated objects in a 3D modelling application that supports X-Plane animations:

`marginal/groundtraffic/distance`
  Distance travelled from the first waypoint, in metres. Resets to zero every time the object passes the first waypoint. See below for how to use this to animate rotating wheels.
`marginal/groundtraffic/speed`
  Speed of the object, in metres/second. This returns zero if the object is stationary for any reason, up to the speed that you specified in the Route statement (converted to [m/s]) if it is moving, or negative if it is backing-up (which is useful for displaying reversing lights).
`marginal/groundtraffic/var[0]` ... `var[9]`
  DataRef whose value is controlled by the “Set” and “Pause ... set” commands.
`marginal/groundtraffic/waypoint/last`
  The last waypoint in the Route (counting from zero) visited by the object.
`marginal/groundtraffic/waypoint/last/distance`
  The distance from the last waypoint, in metres. This returns zero if the object is stationary.
`marginal/groundtraffic/waypoint/next`
  The next waypoint in the Route (counting from zero) visited by the object. This is one greater than the last waypoint unless the object is reversing its route or is circling back to the first waypoint.
`marginal/groundtraffic/waypoint/next/distance`
  The distance to the next waypoint, in metres.

These DataRefs return values specific to the route that the object is following; so they are only useful in animating objects on routes controlled by this plugin, not in static objects that are placed conventionally within an X-Plane scenery package. (You can use the “Set” and/or “Pause ... set” commands with a custom DataRef name to publish DataRefs for use in static objects). You can examine the values of these DataRefs using DataRefEditor, but it will only display the values for the first route listed in `GroundTraffic.txt`.

#### Animating rotating wheels

The speed of rotation of a wheel depends on its size; a smaller wheel needs to rotate more quickly to cover a given distance than a larger wheel. So in your 3D modelling application measure the diameter of the wheel (in metres) and multiple this value by π to obtain the wheel's circumference.

Set up an animation for the wheel containing two keyframes. Rotate the wheel 90° forwards around its axle between the two keyframes. Specify the X-Plane animation values as follows:

- DataRef: `marginal/groundtraffic/distance`
- Keyframe#0: DataRef value = 0.
- Keyframe#1: DataRef value = one quarter of the circumference of the wheel [m].
- Loop DataRef value = the circumference of the wheel [m].

For example, for a wheel 1.155m in diameter:

- DataRef: `marginal/groundtraffic/distance`
- Keyframe#0: DataRef value = 0
- Keyframe#1: DataRef value = 1.155 × π ÷ 4 = 1.155 × 0.7854 = 0.907
- Loop DataRef value = 1.155 × π = 1.155 × 3.1416 = 3.628

### Troubleshooting

You can check that the plugin has been loaded and is operating correctly by opening the file `Log.txt` in the X-Plane folder. You should see:

> Fetching plugins for Custom Scenery/my scenery package/plugins
> Loaded: Custom Scenery/my scenery package/plugins/GroundTraffic/mac.xpl.

If the plugin is operating correctly it produces no other output in the log. However if the plugin has a problem reading your `GroundTraffic.txt` file you will see one or more entries starting with “GroundTraffic:” in the log and the plugin will not perform any animation. For example:

> GroundTraffic: Empty route at line 15

The plugin examines your `GroundTraffic.txt` file when you make a selection from the `Location → Select Global Airport` dialog; if you have edited the file then the plugin will re-read it and re-start the animations. Or you can force the plugin to re-read the file, re-load the objects, and re-start the animations by disabling and re-enabling it in the `Plugin → Plugin Admin → Enable/Disable` dialog.

### Acknowledgements

“X-Plane” is a registered trademark of Laminar Research.

### License

This kit is licensed under the Creative Commons Attribution license. In short, you can use the plugin provided by this kit in commerical scenery packages, but you must give the author credit.

The author would appreciate a courtesy copy of any commercial scenery that you make using this kit, but you are under no obligation.

Version 1.00 © 2013 Jonathan Harris